package com.happok.onlineclass.usermanagement.verifycode.service.impl;

import com.aliyuncs.exceptions.ClientException;
import com.happok.common.exception.BusinessException;
import com.happok.onlineclass.usermanagement.common.exception.DisableException;
import com.happok.onlineclass.usermanagement.common.exception.PreconditionFailedException;
import com.happok.onlineclass.usermanagement.common.exception.RetryWithException;
import com.happok.onlineclass.usermanagement.common.exception.SendException;
import com.happok.onlineclass.usermanagement.setting.dto.EmailSettingInfoDTO;
import com.happok.onlineclass.usermanagement.setting.dto.FunctionVerifyDTO;
import com.happok.onlineclass.usermanagement.setting.dto.VerifySmsDTO;
import com.happok.onlineclass.usermanagement.setting.po.SmsPO;
import com.happok.onlineclass.usermanagement.setting.service.SettingService;
import com.happok.onlineclass.usermanagement.setting.utils.EmailUtil;
import com.happok.onlineclass.usermanagement.setting.utils.SmsUtil;
import com.happok.onlineclass.usermanagement.user.service.ManageUserServiceImpl;
import com.happok.onlineclass.usermanagement.verifycode.dto.FunctionVerifyCodeDTO;
import com.happok.onlineclass.usermanagement.verifycode.dto.TemplateVerifyCodeDTO;
import com.happok.onlineclass.usermanagement.verifycode.service.VerifyCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 接口分类                        yapi 分类文件夹
 * 功能说明
 *
 * @author : chenac
 * @module 项目名称/模块名          yapi 项目组/子项目
 * @date : 2021/10/12 17:14
 */
@Service
public class VerifyCodeServiceImpl implements VerifyCodeService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private SettingService settingService;

    @Override
    public void getFunctionVerifyCode(FunctionVerifyCodeDTO verifyCodeDTO) {
        if ((ObjectUtils.isEmpty(verifyCodeDTO.getPhone())
                && ObjectUtils.isEmpty(verifyCodeDTO.getEmail())) ||
                ObjectUtils.isEmpty(verifyCodeDTO.getFunctionClassify())) {
            throw new BusinessException("参数异常");
        }
        if (!ObjectUtils.isEmpty(verifyCodeDTO.getPhone())) {
            if (!ManageUserServiceImpl.phonePattern.matcher(verifyCodeDTO.getPhone()).matches()) {
                throw new BusinessException("手机号格式不正确");
            }
            // 检测短信服务器配置是否正常，是否启用
            SmsPO smsSetting = settingService.getSmsSettingEnable();
            boolean smsEnable = settingService.smsSettingEnable(smsSetting);
            if (!smsEnable) {
                throw new DisableException("短信服务设置异常");
            }
            // 根据功能查找短信模板
            FunctionVerifyDTO functionSetting = settingService.getFunctionSetting(verifyCodeDTO.getFunctionClassify());
            if (ObjectUtils.isEmpty(functionSetting) || !functionSetting.getOpen() ||
                    ObjectUtils.isEmpty(functionSetting.getSmsVerify().getTemplateCode())) {
                throw new DisableException("功能设置异常");
            }
            Integer verifyCode = getVerifyCode(verifyCodeDTO.getPhone(), verifyCodeDTO.getFunctionClassify().getFunction());
            // 发送验证码
            try {
                SmsUtil.sendSmsVerifyCode(smsSetting.getAccessKeyId(), smsSetting.getAccessKeySecret(),
                        smsSetting.getDomainName(), smsSetting.getSmsSign(), verifyCodeDTO.getPhone(), verifyCode, functionSetting.getSmsVerify().getTemplateCode());
            } catch (ClientException e) {
                deleteVerifyCode(verifyCodeDTO.getPhone(), verifyCodeDTO.getFunctionClassify().getFunction());
                throw new SendException(1, "验证码发送失败");
            }
        }
        if (!ObjectUtils.isEmpty(verifyCodeDTO.getEmail())) {
            if (!ManageUserServiceImpl.emailPattern.matcher(verifyCodeDTO.getEmail()).matches()) {
                throw new BusinessException("邮箱格式不正确");
            }
            // 检测邮箱服务器配置是否正常，是否启用
            EmailSettingInfoDTO emailSetting = settingService.getEmailSetting(false);
            boolean emailEnable = settingService.emailSettingEnable(emailSetting);
            if (!emailEnable) {
                throw new DisableException("邮箱服务设置异常");
            }
            // 根据功能查找邮箱模板
            FunctionVerifyDTO functionSetting = settingService.getFunctionSetting(verifyCodeDTO.getFunctionClassify());
            if (ObjectUtils.isEmpty(functionSetting) || !functionSetting.getOpen() ||
                    ObjectUtils.isEmpty(functionSetting.getEmailVerify().getContent()) ||
                    ObjectUtils.isEmpty(functionSetting.getEmailVerify().getTopic())) {
                throw new DisableException("功能设置异常");
            }
            Integer verifyCode = getVerifyCode(verifyCodeDTO.getEmail(), verifyCodeDTO.getFunctionClassify().getFunction());
            // 发送验证码
            try {
                EmailUtil.sendEmailVerifyCode(emailSetting.getServer(), emailSetting.getAccount(),
                        !ObjectUtils.isEmpty(emailSetting.getPassword()) ? emailSetting.getPassword() : emailSetting.getAuthorizationCode(),
                        verifyCodeDTO.getEmail(), verifyCode, functionSetting.getEmailVerify());
            } catch (Exception e) {
                deleteVerifyCode(verifyCodeDTO.getEmail(), verifyCodeDTO.getFunctionClassify().getFunction());
                throw new SendException(1, e.getLocalizedMessage());
            }
        }
    }

    @Override
    public void getTemplateVerifyCode(TemplateVerifyCodeDTO verifyCodeDTO) {
        if (ObjectUtils.isEmpty(verifyCodeDTO.getPhone()) ||
                ObjectUtils.isEmpty(verifyCodeDTO.getTemplateCode()) ||
                ObjectUtils.isEmpty(verifyCodeDTO.getVariables()) ||
                verifyCodeDTO.getVariables().size() > 2) {
            throw new BusinessException("参数异常");
        }
        if (!ManageUserServiceImpl.phonePattern.matcher(verifyCodeDTO.getPhone()).matches()) {
            throw new BusinessException("手机号格式不正确");
        }
        // 检测短信服务器配置是否正常，是否启用
        SmsPO smsSetting = settingService.getSmsSettingEnable();
        boolean smsEnable = settingService.smsSettingEnable(smsSetting);
        if (!smsEnable) {
            throw new DisableException("短信服务设置异常");
        }
        if (verifyCodeDTO.getVariables().size() == 1) {
            if (verifyCodeDTO.getVariables().get(0).equals("code")) {
                Integer verifyCode = getVerifyCode(verifyCodeDTO.getPhone(), verifyCodeDTO.getVariables().get(0) + verifyCodeDTO.getTemplateCode());
                // 发送验证码
                try {
                    SmsUtil.sendSmsVerifyCode(smsSetting.getAccessKeyId(), smsSetting.getAccessKeySecret(),
                            smsSetting.getDomainName(), smsSetting.getSmsSign(), verifyCodeDTO.getPhone(), verifyCode, verifyCodeDTO.getTemplateCode());
                } catch (ClientException e) {
                    deleteVerifyCode(verifyCodeDTO.getPhone(), verifyCodeDTO.getTemplateCode());
                    throw new SendException(1, "验证码发送失败");
                }
            } else if (verifyCodeDTO.getVariables().get(0).equals("password")) {
                Integer passwordNumber = getVerifyCode(verifyCodeDTO.getPhone(), verifyCodeDTO.getVariables().get(0) + verifyCodeDTO.getTemplateCode());
                // 发送验证码
                try {
                    SmsUtil.sendSmsResetPwd(smsSetting.getAccessKeyId(), smsSetting.getAccessKeySecret(),
                            smsSetting.getDomainName(), smsSetting.getSmsSign(), verifyCodeDTO.getPhone(), passwordNumber.toString(), verifyCodeDTO.getTemplateCode());
                } catch (ClientException e) {
                    deleteVerifyCode(verifyCodeDTO.getPhone(), verifyCodeDTO.getTemplateCode());
                    throw new SendException(1, "验证码发送失败");
                }
            }
        } else {
            Integer accountNumber,passwordNumber;
            if (verifyCodeDTO.getVariables().get(0).equals("password")) {
                passwordNumber = getVerifyCode(verifyCodeDTO.getPhone(), verifyCodeDTO.getVariables().get(0) + verifyCodeDTO.getTemplateCode());
                accountNumber = getVerifyCode(verifyCodeDTO.getPhone(), verifyCodeDTO.getVariables().get(1) + verifyCodeDTO.getTemplateCode());
            } else {
                accountNumber = getVerifyCode(verifyCodeDTO.getPhone(), verifyCodeDTO.getVariables().get(0) + verifyCodeDTO.getTemplateCode());
                passwordNumber = getVerifyCode(verifyCodeDTO.getPhone(), verifyCodeDTO.getVariables().get(1) + verifyCodeDTO.getTemplateCode());
            }
            // 发送验证码
            try {
                SmsUtil.sendSmsAddUser(smsSetting.getAccessKeyId(), smsSetting.getAccessKeySecret(),
                        smsSetting.getDomainName(), smsSetting.getSmsSign(), verifyCodeDTO.getPhone(), accountNumber.toString(), passwordNumber.toString(), verifyCodeDTO.getTemplateCode());
            } catch (ClientException e) {
                deleteVerifyCode(verifyCodeDTO.getPhone(), verifyCodeDTO.getTemplateCode());
                throw new SendException(1, "验证码发送失败");
            }
        }
    }

    @Override
    public void verificationSms(VerifySmsDTO verifySms) {
        for (Map.Entry<String, Integer> entry : verifySms.getVerifyCodeMap().entrySet()) {
            Integer redisVerifyCode = verifyCode(verifySms.getPhone(), entry.getKey() + verifySms.getTemplateCode());
            if (ObjectUtils.isEmpty(redisVerifyCode)) {
                throw new PreconditionFailedException(2, "验证码失效");
            }
            if (!entry.getValue().equals(redisVerifyCode)) {
                throw new PreconditionFailedException(1, "验证码不正确");
            } else {
                deleteVerifyCode(verifySms.getPhone(), entry.getKey() + verifySms.getTemplateCode());
            }
        }
    }

    @Override
    public Integer verifyCode(String account, String function) {
        ValueOperations<String, String> forValue = redisTemplate.opsForValue();
        String redisVerifyCode = forValue.get(getKey(account, function));
        if (ObjectUtils.isEmpty(redisVerifyCode)) {
            return null;
        } else {
            return Integer.parseInt(redisVerifyCode);
        }
    }

    @Override
    public void deleteVerifyCode(String account, String function) {
        redisTemplate.delete(getKey(account, function));
    }

    @Override
    public void checkVerifyCode(String account, String function, Integer verifyCode) {
        // 根据账号和功能查询验证码
        Integer redisVerifyCode = verifyCode(account, function);
        if (ObjectUtils.isEmpty(redisVerifyCode)) {
            throw new PreconditionFailedException(2, "验证码失效");
        }
        if (!verifyCode.equals(redisVerifyCode)) {
            throw new PreconditionFailedException(1, "验证码不正确");
        } else {
            deleteVerifyCode(account, function);
        }
    }

    @Override
    public String getIntegerRandom() {
        StringBuilder str = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 6; i++) {
            int tmp = random.nextInt(10);
            if (i == 0) {
                while (tmp == 0) {
                    tmp = random.nextInt(10);
                }
            }
            str.append(tmp);
        }
        return str.toString();
    }

    @Override
    public Integer getVerifyCode(String account, String function) {
        ValueOperations<String, String> forValue = redisTemplate.opsForValue();
        String redisVerifyCode = forValue.get(getKey(account, function));
        if (ObjectUtils.isEmpty(redisVerifyCode)) {
            return createVerifyCode(account, function);
        } else {
            Long expire = redisTemplate.getExpire(getKey(account, function));
            throw new RetryWithException("" + expire);
        }
    }

    private Integer createVerifyCode(String account, String function) {
        String validateCode = getIntegerRandom();
        ValueOperations forValue = redisTemplate.opsForValue();
        forValue.set(getKey(account, function), validateCode);
        redisTemplate.expire(getKey(account, function), 3, TimeUnit.MINUTES);
        return Integer.parseInt(validateCode);
    }

    private String getKey(String account, String function) {
        return account + "_" + function;
    }
}